#include "QuadTreeNode.h"
#include "QuadTree.h"

using namespace QuadTreeLib;

void QuadTreeNode::createChildren(size_t lodLevel)
{
	AABB aabb;
	float halfSize = mSize * 0.5f;
	float cx = (mAABB.Min.x + mAABB.Max.x) * 0.5f;
	float cz = (mAABB.Min.z + mAABB.Max.z) * 0.5f;
	
	aabb.Min = Vector3(mAABB.Min.x, mAABB.Min.y, mAABB.Min.z); 
	aabb.Max = Vector3(cx, mAABB.Max.y, cz);
	mChild[0] = new QuadTreeNode(lodLevel, halfSize, aabb);
	aabb.Min = Vector3(cx, mAABB.Min.y, mAABB.Min.z); 
	aabb.Max = Vector3(mAABB.Max.x ,mAABB.Max.y, cz);
	mChild[1] = new QuadTreeNode(lodLevel, halfSize, aabb);
	aabb.Min = Vector3(mAABB.Min.x, mAABB.Min.y, cz); 
	aabb.Max = Vector3(cx ,mAABB.Max.y, mAABB.Max.z);
	mChild[2] = new QuadTreeNode(lodLevel, halfSize, aabb);
	aabb.Min = Vector3(cx, mAABB.Min.y, cz); 
	aabb.Max = Vector3(mAABB.Max.x ,mAABB.Max.y, mAABB.Max.z);
	mChild[3] = new QuadTreeNode(lodLevel, halfSize, aabb);
}

QuadTreeNode::LODSelectResult QuadTreeNode::LODSelect(LODSelection& lodSelection, bool parentInFrustum)
{
	// check frustum culling
	AABB::IntersectType inFrustum = parentInFrustum ? AABB::IT_Inside : mAABB.TestInBoundingPlanes(lodSelection.mFrustums);
	if (AABB::IT_Outside == inFrustum)
		return LOD_OutOfFrustum;
	
	float range = lodSelection.mLodRanges[mLodLevel];

	if( !mAABB.IntersectSphereSq(lodSelection.mCamPos, range*range) )
		return LOD_OutOfRange;

	LODSelectResult subTLSelRes = LOD_Undefined;
	LODSelectResult subTRSelRes = LOD_Undefined;
	LODSelectResult subBLSelRes = LOD_Undefined;
	LODSelectResult subBRSelRes = LOD_Undefined;

	if (mLodLevel != 0)
	{
		float nextLodRange = lodSelection.mLodRanges[mLodLevel-1];
		if ( !mAABB.IntersectSphereSq(lodSelection.mCamPos, nextLodRange*nextLodRange) )
		{
			bool isInFrustum = AABB::IT_Inside == inFrustum;

			if (mChild[0] == 0)
			{
				createChildren(mLodLevel-1);
			}

			subTLSelRes = mChild[0]->LODSelect(lodSelection, isInFrustum);
			subTRSelRes = mChild[1]->LODSelect(lodSelection, isInFrustum);
			subBLSelRes = mChild[2]->LODSelect(lodSelection, isInFrustum);
			subBRSelRes = mChild[3]->LODSelect(lodSelection, isInFrustum);
		}
	}

	bool bRemoveSubTL = (subTLSelRes == LOD_OutOfFrustum) || (subTLSelRes == LOD_Selected);
	bool bRemoveSubTR = (subTRSelRes == LOD_OutOfFrustum) || (subTRSelRes == LOD_Selected);
	bool bRemoveSubBL = (subBLSelRes == LOD_OutOfFrustum) || (subBLSelRes == LOD_Selected);
	bool bRemoveSubBR = (subBRSelRes == LOD_OutOfFrustum) || (subBRSelRes == LOD_Selected);

	if (!(bRemoveSubTL && bRemoveSubTR && bRemoveSubBL && bRemoveSubBR))
	{
		// add current node
		lodSelection.mSelectedNodes.push_back(this);

		return LOD_Selected;
	}

	// if any of child nodes are selected, then return selected - otherwise all of them are out of frustum, so we're out of frustum too
	if( (subTLSelRes == LOD_Selected) || (subTRSelRes == LOD_Selected) || (subBLSelRes == LOD_Selected) || (subBRSelRes == LOD_Selected) )
		return LOD_Selected;

	return LOD_OutOfFrustum;
}